﻿

//#define ONLINE_SFINAE_CPP11

#ifndef ONLINE_SFINAE_CPP11
    #pragma once
#endif

#include <type_traits>

#define DETECTED_MEMBER(member_name)                                                      \
template<typename C, typename T>class has_##member_name                                   \
{                                                                                         \
    template<class U> static auto Check(U*) -> typename                                   \
        std::is_same< decltype( std::declval<U>().member_name), T >::type;                \
                                                                                          \
    template<typename> static std::false_type Check(...);                                 \
                                                                                          \
    typedef decltype(Check<C>(0)) Result;                                                 \
public:                                                                                   \
    enum { eIS_NOCONST = 0 };                                                             \
    enum { eIS_CONST   = 0 };                                                             \
    enum { eIS_AVAILABLE = Result::value };                                               \
};                                                                                        \
template<class C,typename R, typename... Args>class has_##member_name<C, R(Args...)>      \
{                                                                                         \
    typedef R(C::*Method)(Args...);                                                       \
    typedef R(C::*CMethod)(Args...)const;                                                 \
    template <class U> using NoConst = decltype(static_cast<Method> (&U::member_name));   \
    template <class U> using Const   = decltype(static_cast<CMethod>(&U::member_name));   \
                                                                                          \
    template<class U> static std::true_type  CheckNoConst( NoConst<U>*p );                \
    template<class>   static std::false_type CheckNoConst(...);                           \
                                                                                          \
    template<class U> static std::true_type  CheckConst( Const<U>*p);                     \
    template<class>   static std::false_type CheckConst(...);                             \
                                                                                          \
    typedef decltype(CheckConst<C>(0))   ConstResult;                                     \
    typedef decltype(CheckNoConst<C>(0)) NoConstResult;                                   \
public:                                                                                   \
    enum { eIS_NOCONST = NoConstResult::value       };                                    \
    enum { eIS_CONST   = ConstResult::value         };                                    \
    enum { eIS_AVAILABLE = eIS_NOCONST || eIS_CONST };                                    \
};
//------------------------------------------------------------------------------------

#define is_available(class_name, member_type, member_name)         \
    has_##member_name<class_name, member_type>::eIS_AVAILABLE

#define is_available_method(class_name, member_type, member_name)  \
    has_##member_name<class_name, member_type>::eIS_NOCONST

#define is_available_cmethod(class_name, member_type, member_name) \
    has_##member_name<class_name, member_type>::eIS_CONST

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------


#ifdef ONLINE_SFINAE_CPP11

#include <type_traits>

int g;
struct A
{
    A():r(g){}

    void fooA(int);
    void fooA(int)const;
    void fooA();
    void fooA()const;

    void foo();
    void foo()const;

    int v;
    int& r;
    int a[10];
    int* p;
    int** pp;
private:
    void private_foo();
};

struct B : A
{
    int v;
    int fooB(){ return 0; }
};

DETECTED_MEMBER(foo) ;
DETECTED_MEMBER(fooA);
DETECTED_MEMBER(fooB);
DETECTED_MEMBER(v);
DETECTED_MEMBER(r);
DETECTED_MEMBER(a);
DETECTED_MEMBER(p);
DETECTED_MEMBER(pp);
DETECTED_MEMBER(private_foo);

#include<iostream>
using namespace std;

int main()
{
    //синтаксис:  is_available(  имя_класса ,  тип_мембера  , имя_мембера)
    //вернет true если мембер доступен, иначе - false

    //синтаксис:  is_available_method(  имя_класса ,  тип_мембера  , имя_мембера)
    //вернет true если доступна не константная версия функции-члена

    //синтаксис:  is_available_cmethod(  имя_класса ,  тип_мембера  , имя_мембера)
    //вернет true если доступна константная версия функции-члена

    //---- детектирование данных
    cout<< "is available int B.v ? (most be 1) "
        << is_available(B,int,v)     <<endl;

    cout<< "is available int& B.r ?  (most be 1) "
        << is_available(B,int&,r)    <<endl;

    cout<< "is available int B.a[10] ?  (most be 1) "
        << is_available(B,int[10],a) <<endl;

    cout<< "is available int* B.p ?  (most be 1) "
        << is_available(B,int*,p)    <<endl;

    cout<< "is available int* B.pp ?  (most be 1) "
        << is_available(B,int**,pp)  <<endl;

    //---- детектирование методов
    cout<< "is available void B.fooA(int) or void B::fooA(int)const ?  (most be 1) "
        <<is_available(B,void(int),fooA) <<endl;

    cout<< "is available void B.fooA(int) ?  (most be 1) "
        << is_available_method(B,void(int),fooA) <<endl;

    cout<< "is available void B.fooA(int)const ?  (most be 1) "
        << is_available_cmethod(B,void(int),fooA) <<endl;

    cout<< "is available int B.fooB() ?  (most be 1) "
        << is_available_method(B,int(),fooB) <<endl;

    cout<< "is available void B.private_foo() ?  (most be 0) "    
        << is_available(B,void(),private_foo) <<endl;

    cout<< "is available void B.foo(int) ?  (most be 0) "    
        << is_available(B,void(int),foo) <<endl;
}


#endif
